﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using coffeemaker.contracts;

namespace coffeemaker.simulation
{
    public class CoffeeMakerSimulation : ICoffeeMakerSimulation
    {
        private Timer _simulationLoop;

        private CoffeeMakerState _state;


        public void Start()
        {
            Trace.TraceInformation("CoffeeMakerSimulation.Start");

            if (_state == null) _state = new CoffeeMakerState { potOnWarmer = true };
            _simulationLoop = new Timer(Simulate, null, 0, 500);
        }

        public void Pause()
        {
            Trace.TraceInformation("CoffeeMakerSimulation.Pause");
            StopSimulationLoop();
        }

        public void Stop()
        {
            Trace.TraceInformation("CoffeeMakerSimulation.Stop");
            StopSimulationLoop();
            _state = null;
        }

        private void StopSimulationLoop()
        {
            if (_simulationLoop == null) return;
            _simulationLoop.Change(Timeout.Infinite, Timeout.Infinite);
        }

        public void Dispose()
        {
            Trace.TraceInformation("CoffeeMakerSimulation.Dispose");
            this.Stop();
        }


        public void SwitchBoiler(bool on)
        {
            Trace.TraceInformation("CoffeeMakerSimulation.SwitchBoiler: {0}", on);
            _state.boilerOn = on;
        }

        public void FillBoiler()
        {
            Trace.TraceInformation("CoffeeMakerSimulation.FillBoiler");
            if (_state.boilerPercentFilled > 0 && _state.potPercentFilled > 0)
                Trace.TraceWarning("Filling non empty boiler while pot is not empty risks overflowing the pot!");
            _state.boilerPercentFilled = 100;
        }

        public void SwitchWarmer(bool on)
        {
            Trace.TraceInformation("CoffeeMakerSimulation.SwitchWarmer: {0}", on);
            _state.warmerOn = on;
        }

        public void PlacePot(bool onWarmer)
        {
            Trace.TraceInformation("CoffeeMakerSimulation.PlacePot: {0}", onWarmer);
            _state.potOnWarmer = onWarmer;
        }

        public void EmptyPot()
        {
            Trace.TraceInformation("CoffeeMakerSimulation.EmptyPot");
            if (!_state.potOnWarmer)
                _state.potPercentFilled = 0;
            else
                Trace.TraceWarning("Cannot empty pot while placed on warmer!");
        }

        public void PressBrewButton()
        {
            Trace.TraceInformation("CoffeeMakerSimulation.PressBrewButton");
            _state.brewButtonPushed = !_state.brewButtonPushed;
        }

        public void SwitchValve(bool open)
        {
            Trace.TraceInformation("CoffeeMakerSimulation.SwitchValve: {0}", open);
            _state.valveOpen = open;
        }


        public event Action<CoffeeMakerState> OnStateChange;


        private void Simulate(object _)
        {
            var amout_of_water_flowing_out_of_boiler = 0;

            if (_state.boilerOn && _state.boilerPercentFilled > 0) amout_of_water_flowing_out_of_boiler = (int)(_state.boilerPercentFilled * 0.05);

            UpdateBoiler(amout_of_water_flowing_out_of_boiler);
            UpdateValve(amout_of_water_flowing_out_of_boiler);
            UpdateWarmer(amout_of_water_flowing_out_of_boiler);
            UpdateIndicatorLight();

            this.OnStateChange(_state);
        }

        private void UpdateBoiler(int heatedWater)
        {
            _state.boilerPercentFilled -= heatedWater;
            if (_state.boilerPercentFilled < 0) _state.boilerPercentFilled = 0;

            _state.boilerOverheated = _state.boilerOn && heatedWater == 0 && _state.boilerPercentFilled <= 0;
        }

        private void UpdateValve(int hotWater)
        {
            _state.steamBlowingOffThroughOpenValve = hotWater > 0 && _state.valveOpen;
        }

        private void UpdateWarmer(int coffee)
        {
            if (!_state.steamBlowingOffThroughOpenValve)
            {
                _state.potPercentFilled += coffee;
                if (_state.potPercentFilled > 100) _state.potPercentFilled = 100;
            }

            _state.waterSpillingOnEmptyWarmer = coffee > 0 && !_state.valveOpen && !_state.potOnWarmer;
            _state.warmerOverheated = _state.warmerOn && (!_state.potOnWarmer || _state.potPercentFilled == 0);
        }

        private void UpdateIndicatorLight()
        {
            _state.indicatorOn = _state.boilerOn || _state.warmerOn;
        }
    }
}
